1 /*
2 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.io;
27
28 import java.io.UnsupportedEncodingException;
29 import java.lang.ref.SoftReference;
30 import java.util.Properties;
31
32 /**
33 * Package-private utility class that caches the default converter classes and
34 * provides other logic common to both the ByteToCharConverter and
35 * CharToByteConverter classes.
36 *
37 * @author Mark Reinhold
38 * @since 1.2
39 *
40 * @deprecated Replaced by {@link java.nio.charset}. THIS API WILL BE
41 * REMOVED IN J2SE 1.6.
42 */
43 @Deprecated
44 public class Converters {
45
46 private Converters() { } /* To prevent instantiation */
47
48 /* Lock for all static fields in this class */
49 private static Object lock = Converters.class;
50
51 /* Cached values of system properties */
52 private static String converterPackageName = null; /* file.encoding.pkg */
53 private static String defaultEncoding = null; /* file.encoding */
54
55 /* Converter type constants and names */
56 public static final int BYTE_TO_CHAR = 0;
57 public static final int CHAR_TO_BYTE = 1;
58 private static final String[] converterPrefix = { "ByteToChar",
59 "CharToByte" };
60
61
62 // -- Converter class cache --
63
64 private static final int CACHE_SIZE = 3;
65
66 /* For the default charset, whatever it turns out to be */
67 private static final Object DEFAULT_NAME = new Object();
68
69 /* Cached converter classes, CACHE_SIZE per converter type. Each cache
70 * entry is a soft reference to a two-object array; the first element of
71 * the array is the converter class, the second is an object (typically a
72 * string) representing the encoding name that was used to request the
73 * converter, e.g.,
74 *
75 * ((Object[])classCache[CHAR_TO_BYTE][i].get())[0]
76 *
77 * will be a CharToByteConverter and
78 *
79 * ((Object[])classCache[CHAR_TO_BYTE][i].get())[1]
80 *
81 * will be the string encoding name used to request it, assuming that cache
82 * entry i is valid.
83 *
84 * Ordinarily we'd do this with a private static utility class, but since
85 * this code can be involved in the startup sequence it's important to keep
86 * the footprint down.
87 */
88 @SuppressWarnings("unchecked")
89 private static SoftReference<Object[]>[][] classCache
90 = (SoftReference<Object[]>[][]) new SoftReference<?>[][] {
91 new SoftReference<?>[CACHE_SIZE],
92 new SoftReference<?>[CACHE_SIZE]
93 };
94
95 private static void moveToFront(Object[] oa, int i) {
96 Object ob = oa[i];
97 for (int j = i; j > 0; j--)
98 oa[j] = oa[j - 1];
99 oa[0] = ob;
100 }
101
102 private static Class<?> cache(int type, Object encoding) {
103 SoftReference<Object[]>[] srs = classCache[type];
104 for (int i = 0; i < CACHE_SIZE; i++) {
105 SoftReference<Object[]> sr = srs[i];
106 if (sr == null)
107 continue;
108 Object[] oa = sr.get();
109 if (oa == null) {
110 srs[i] = null;
111 continue;
112 }
113 if (oa[1].equals(encoding)) {
114 moveToFront(srs, i);
115 return (Class<?>)oa[0];
116 }
117 }
118 return null;
119 }
120
121 private static Class<?> cache(int type, Object encoding, Class<?> c) {
122 SoftReference<Object[]>[] srs = classCache[type];
123 srs[CACHE_SIZE - 1] = new SoftReference<>(new Object[] { c, encoding });
124 moveToFront(srs, CACHE_SIZE - 1);
125 return c;
126 }
127
128 /* Used to avoid doing expensive charset lookups for charsets that are not
129 * yet directly supported by NIO.
130 */
131 public static boolean isCached(int type, String encoding) {
132 synchronized (lock) {
133 SoftReference<Object[]>[] srs = classCache[type];
134 for (int i = 0; i < CACHE_SIZE; i++) {
135 SoftReference<Object[]> sr = srs[i];
136 if (sr == null)
137 continue;
138 Object[] oa = sr.get();
139 if (oa == null) {
140 srs[i] = null;
141 continue;
142 }
143 if (oa[1].equals(encoding))
144 return true;
145 }
146 return false;
147 }
148 }
149
150
151
152 /** Get the name of the converter package */
153 private static String getConverterPackageName() {
154 String cp = converterPackageName;
155 if (cp != null) return cp;
156 java.security.PrivilegedAction<String> pa =
157 new sun.security.action.GetPropertyAction("file.encoding.pkg");
158 cp = java.security.AccessController.doPrivileged(pa);
159 if (cp != null) {
160 /* Property is set, so take it as the true converter package */
161 converterPackageName = cp;
162 } else {
163 /* Fall back to sun.io */
164 cp = "sun.io";
165 }
166 return cp;
167 }
168
169 public static String getDefaultEncodingName() {
170 synchronized (lock) {
171 if (defaultEncoding == null) {
172 java.security.PrivilegedAction<String> pa =
173 new sun.security.action.GetPropertyAction("file.encoding");
174 defaultEncoding = java.security.AccessController.doPrivileged(pa);
175 }
176 }
177 return defaultEncoding;
178 }
179
180 public static void resetDefaultEncodingName() {
181 // This method should only be called during VM initialization.
182 if (sun.misc.VM.isBooted())
183 return;
184
185 synchronized (lock) {
186 defaultEncoding = "ISO-8859-1";
187 Properties p = System.getProperties();
188 p.setProperty("file.encoding", defaultEncoding);
189 System.setProperties(p);
190 }
191 }
192
193 /**
194 * Get the class that implements the given type of converter for the named
195 * encoding, or throw an UnsupportedEncodingException if no such class can
196 * be found
197 */
198 private static Class<?> getConverterClass(int type, String encoding)
199 throws UnsupportedEncodingException
200 {
201 String enc = null;
202
203 /* "ISO8859_1" is the canonical name for the ISO-Latin-1 encoding.
204 Native code in the JDK commonly uses the alias "8859_1" instead of
205 "ISO8859_1". We hardwire this alias here in order to avoid loading
206 the full alias table just for this case. */
207 if (!encoding.equals("ISO8859_1")) {
208 if (encoding.equals("8859_1")) {
209 enc = "ISO8859_1";
210 /*
211 * On Solaris with nl_langinfo() called in GetJavaProperties():
212 *
213 * locale undefined -> NULL -> hardcoded default
214 * "C" locale -> "" -> hardcoded default (on 2.6)
215 * "C" locale -> "646" (on 2.7)
216 * "en_US" locale -> "ISO8859-1"
217 * "en_GB" locale -> "ISO8859-1" (on 2.7)
218 * "en_UK" locale -> "ISO8859-1" (on 2.6)
219 */
220 } else if (encoding.equals("ISO8859-1")) {
221 enc = "ISO8859_1";
222 } else if (encoding.equals("646")) {
223 enc = "ASCII";
224 } else {
225 enc = CharacterEncoding.aliasName(encoding);
226 }
227 }
228 if (enc == null) {
229 enc = encoding;
230 }
231
232 try {
233 return Class.forName(getConverterPackageName()
234 + "." + converterPrefix[type] + enc);
235 } catch(ClassNotFoundException e) {
236 throw new UnsupportedEncodingException(enc);
237 }
238
239 }
240
241 /**
242 * Instantiate the given converter class, or throw an
243 * UnsupportedEncodingException if it cannot be instantiated
244 */
245 private static Object newConverter(String enc, Class<?> c)
246 throws UnsupportedEncodingException
247 {
248 try {
249 return c.newInstance();
250 } catch(InstantiationException e) {
251 throw new UnsupportedEncodingException(enc);
252 } catch(IllegalAccessException e) {
253 throw new UnsupportedEncodingException(enc);
254 }
255 }
256
257 /**
258 * Create a converter object that implements the given type of converter
259 * for the given encoding, or throw an UnsupportedEncodingException if no
260 * appropriate converter class can be found and instantiated
261 */
262 static Object newConverter(int type, String enc)
263 throws UnsupportedEncodingException
264 {
265 Class<?> c;
266 synchronized (lock) {
267 c = cache(type, enc);
268 if (c == null) {
269 c = getConverterClass(type, enc);
270 if (!c.getName().equals("sun.io.CharToByteUTF8"))
271 cache(type, enc, c);
272 }
273 }
274 return newConverter(enc, c);
275 }
276
277 /**
278 * Find the class that implements the given type of converter for the
279 * default encoding. If the default encoding cannot be determined or is
280 * not yet defined, return a class that implements the fallback default
281 * encoding, which is just ISO 8859-1.
282 */
283 private static Class<?> getDefaultConverterClass(int type) {
284 boolean fillCache = false;
285 Class<?> c;
286
287 /* First check the class cache */
288 c = cache(type, DEFAULT_NAME);
289 if (c != null)
290 return c;
291
292 /* Determine the encoding name */
293 String enc = getDefaultEncodingName();
294 if (enc != null) {
295 /* file.encoding has been set, so cache the converter class */
296 fillCache = true;
297 } else {
298 /* file.encoding has not been set, so use a default encoding which
299 will not be cached */
300 enc = "ISO8859_1";
301 }
302
303 /* We have an encoding name; try to find its class */
304 try {
305 c = getConverterClass(type, enc);
306 if (fillCache) {
307 cache(type, DEFAULT_NAME, c);
308 }
309 } catch (UnsupportedEncodingException x) {
310 /* Can't find the default class, so fall back to ISO 8859-1 */
311 try {
312 c = getConverterClass(type, "ISO8859_1");
313 } catch (UnsupportedEncodingException y) {
314 throw new InternalError("Cannot find default "
315 + converterPrefix[type]
316 + " converter class");
317 }
318 }
319 return c;
320
321 }
322
323 /**
324 * Create a converter object that implements the given type of converter
325 * for the default encoding, falling back to ISO 8859-1 if the default
326 * encoding cannot be determined.
327 */
328 static Object newDefaultConverter(int type) {
329 Class<?> c;
330 synchronized (lock) {
331 c = getDefaultConverterClass(type);
332 }
333 try {
334 return newConverter("", c);
335 } catch (UnsupportedEncodingException x) {
336 throw new InternalError("Cannot instantiate default converter"
337 + " class " + c.getName());
338 }
339 }
340
341 }